#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <iostream>
#include <string>
#include <vector>

int g_val = 0;

class TestMyCons {
 public:
  TestMyCons() {
    g_val++;
  }

  void print() {
    printf("Test this: %d.\n", g_val);
  }
};

TestMyCons g_TMC;

struct FooBar {
  int a;
  int b;
};

std::vector<FooBar> g_chars;
std::string g_string;

void ThrowMyExcept() {
  throw (int) 42;
}

int MissIt() {
  try {
    ThrowMyExcept();
  }
  catch(char* msg) {
    printf("Throwing a string?\n");
    return 0;
  }
  return 1;
}

int CatchIt() {
  try {
    MissIt();
  }
  catch (int e) {
    printf("Caught exception.\n");
    return e;
  }
  catch (...) {
    printf("Got the default.\n");
  }
  return 3;
}

int ReturnMod(int x, int y) {
  return x % y;
}

void FailedArgTest(int argc, char* argv[]) {
  fprintf(stdout, "Failed Arg Test, expecting: <NEXE> 1 2 3 4.\n");
  printf("Received %d args:\n\t", argc);
  for (int a=0; a < argc; a++)
    printf("%s ", argv[a]);
  printf("\n");
  exit(2);
}

void WriteIt(const char *str) {
  write(1, str, strlen(str));
}


int jump_state = 0;

int SJ2(jmp_buf jb) {
  jump_state = 1;
  longjmp(jb, 0);
}

int SJ1(jmp_buf jb) {
  SJ2(jb);
}

void TestSetJump() {
  jmp_buf jb;

  int val = setjmp(jb);
  if (val != jump_state) {
    if (jump_state == 0)
      WriteIt("Expecting set jump.\n");
    else
      WriteIt("Expecting long jump.\n");
    exit(1);
  }

  if (jump_state == 0) {
    jump_state = 1;
    SJ1(jb);
    WriteIt("Unextected return.\n");
    exit(1);
  } else if (jump_state == 1) {
    WriteIt("SetJump Passed\n");
    return;
  } else {
    WriteIt("Unexpected state.\n");
    exit(1);
  }  
}


int main(int argc, char* argv[]) {
   if (g_val == 1) {
    WriteIt("Global constructor fired.\n");
  } else {
    WriteIt("Global constructor failed.\n");
    exit(3);
  }

  fprintf(stdout, "Starting.\n");
  fflush(stdout);

  printf("Hello World.\n");
  fflush(stdout);
 
  WriteIt("^^^ Hello? Flushed StdOut\n");
  write(1, "Past FPRINTF.\n\n", 15);

  TestSetJump();
  
  g_TMC.print();
  std::cout << "Printing with COUT.\n";

  // Test that arguments are correctly passed
  for (int a=1; a < argc; a++) {
    printf("Processing Arg %d.\n", a);
    if (a != atoi(argv[a]))
      FailedArgTest(argc, argv);
  }

  if (argc != 5)
    FailedArgTest(argc, argv);

  fflush(stdout);
  WriteIt("\nAbout to CatchIt.\n");
  int val = CatchIt();
  if (val != 42) {
    printf("Failed Exception test.\n");
    exit(2);
  }

  int x = ReturnMod(7,5);

  printf("PASSED.\n");
  exit(0);
}
